use serde::{Deserialize, Serialize};

use crate::types::{
    mcp::MCPListToolsTool,
    realtime::{ErrorCodeMessage, ErrorMessage},
};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SystemMessageContent {
    /// The text content.
    pub text: String,
    /// The content type. Always `input_text` for system messages.
    pub r#type: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeConversationItemMessageSystem {
    /// The content of the message.
    pub content: Vec<SystemMessageContent>,

    /// The unique ID of the item. This may be provided by the client or generated by the server.
    pub id: Option<String>,

    /// Identifier for the API object being returned - always `realtime.item`.
    /// Optional when creating a new item.
    pub object: Option<String>,

    /// The status of the item. Has no effect on the conversation.
    pub status: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserMessageContentInputText {
    /// The text content (for `input_text`).
    pub text: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserMessageContentInputAudio {
    /// Base64-encoded audio bytes (for `input_audio`), these will be parsed as the
    /// format specified in the session input audio type configuration.
    /// This defaults to PCM 16-bit 24kHz mono if not specified.
    pub audio: String,
    /// Transcript of the audio (for `input_audio`). This is not sent to the model,
    /// but will be attached to the message item for reference.
    pub transcript: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(rename_all = "snake_case")]
pub enum ImageDetail {
    #[default]
    Auto,
    Low,
    High,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserMessageContentInputImage {
    /// Base64-encoded image bytes (for `input_image`) as a data URI.
    /// For example `...`.
    /// Supported formats are PNG and JPEG.
    pub image_url: String,
    /// The detail level of the image (for `input_image`). `auto` will default to `high`.
    pub detail: ImageDetail,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum UserMessageContent {
    InputText(UserMessageContentInputText),
    InputAudio(UserMessageContentInputAudio),
    InputImage(UserMessageContentInputImage),
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeConversationItemMessageUser {
    /// The content of the message.
    pub content: Vec<UserMessageContent>,

    /// The unique ID of the item. This may be provided by the client or generated by the server.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    /// Identifier for the API object being returned - always `realtime.item`.
    /// Optional when creating a new item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub object: Option<String>,

    /// The status of the item. Has no effect on the conversation.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AssistantMessageContentOutputText {
    /// The text content
    pub text: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AssistantMessageContentOutputAudio {
    /// Base64-encoded audio bytes, these will be parsed as the format specified
    /// in the session output audio type configuration. This defaults to PCM 16-bit
    /// 24kHz mono if not specified.
    pub audio: Option<String>,
    /// The transcript of the audio content, this will always be present if the
    /// output type is `audio`.
    pub transcript: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum AssistantMessageContent {
    OutputText(AssistantMessageContentOutputText),
    OutputAudio(AssistantMessageContentOutputAudio),
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeConversationItemMessageAssistant {
    /// The content of the message.
    pub content: Vec<AssistantMessageContent>,

    /// The unique ID of the item. This may be provided by the client or generated by the server.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    /// Identifier for the API object being returned - always `realtime.item`.
    /// Optional when creating a new item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub object: Option<String>,

    /// The status of the item. Has no effect on the conversation.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "role")]
#[serde(rename_all = "lowercase")]
pub enum RealtimeConversationItemMessage {
    System(RealtimeConversationItemMessageSystem),
    User(RealtimeConversationItemMessageUser),
    Assistant(RealtimeConversationItemMessageAssistant),
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeConversationItemFunctionCall {
    /// The arguments of the function call. This is a JSON-encoded string representing
    /// the arguments passed to the function, for example {"arg1": "value1", "arg2": 42}.
    pub arguments: String,

    /// The name of the function being called.
    pub name: String,

    /// The ID of the function call.
    pub call_id: String,

    /// The unique ID of the item. This may be provided by the client or generated by the server.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    /// Identifier for the API object being returned - always `realtime.item`.
    /// Optional when creating a new item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub object: Option<String>,

    /// The status of the item. Has no effect on the conversation.
    pub status: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeConversationItemFunctionCallOutput {
    /// The ID of the function call this output is for.
    pub call_id: String,

    /// The output of the function call, this is free text and can contain any information
    /// or simply be empty.
    pub output: String,

    /// The unique ID of the item. This may be provided by the client or generated by the server.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    /// Identifier for the API object being returned - always `realtime.item`.
    /// Optional when creating a new item.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub object: Option<String>,

    /// The status of the item. Has no effect on the conversation.
    pub status: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeMCPApprovalResponse {
    /// The ID of the approval request being answered.
    pub approval_request_id: String,

    /// Whether the request was approved.
    pub approved: bool,

    /// The unique ID of the approval response.
    pub id: String,

    /// Optional reason for the decision.
    pub reason: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeMCPListTools {
    /// The label of the MCP server.
    pub server_label: String,

    /// The tools available on the server.
    pub tools: Vec<MCPListToolsTool>,

    /// The unique ID of the list.
    pub id: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeMCPApprovalRequest {
    /// A JSON string of arguments for the tool.
    pub arguments: String,

    /// The unique ID of the approval request.
    pub id: String,

    /// The name of the tool to run.
    pub name: String,

    /// The label of the MCP server making the request.
    pub server_label: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeMCPProtocolError {}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RealtimeMCPToolCallError {
    ProtocolError(ErrorCodeMessage),
    ToolExecutionError(ErrorMessage),
    HttpError(ErrorCodeMessage),
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RealtimeMCPToolCall {
    /// A JSON string of the arguments passed to the tool.
    pub arguments: String,

    /// The unique ID of the tool call.
    pub id: String,

    /// The name of the tool that was run.
    pub name: String,

    /// The label of the MCP server running the tool.
    pub server_label: String,

    /// The ID of an associated approval request, if any.
    pub approval_request_id: Option<String>,

    /// The error from the tool call, if any.
    pub error: Option<RealtimeMCPToolCallError>,

    /// The output from the tool call.
    pub output: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RealtimeConversationItem {
    Message(RealtimeConversationItemMessage),
    FunctionCall(RealtimeConversationItemFunctionCall),
    FunctionCallOutput(RealtimeConversationItemFunctionCallOutput),
    McpApprovalResponse(RealtimeMCPApprovalResponse),
    McpListTools(RealtimeMCPListTools),
    McpCall(RealtimeMCPToolCall),
    McpApprovalRequest(RealtimeMCPApprovalRequest),
}

impl TryFrom<serde_json::Value> for RealtimeConversationItem {
    type Error = serde_json::Error;

    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
        serde_json::from_value(value)
    }
}
